/*
* Description: 存储进程生命周期信息
*/
#include "process_detect.h"
#include <mutex>
#include "log.h"
#include "text_util/string_proc.h"
#include "performance/utils/perf_utils.h"
#include "cJSON.h"

namespace {
    const int HOLD_PROCESS_DATA_TIME = 360;
    const std::string PERF_SOFTWARE_WHITE_LIST_PATH = "C:\\ProgramData\\VMAgent\\config\\PerfSoftwareList.json";
    const std::string WIN_UPDATE_CORE_PROCESS = "wuaucltcore.exe";
}
namespace DFX {
void ProcessDetect::Init(std::shared_ptr<WINCONTROL::SendToHost> sendtoHost)
{
    if (sendtoHost == nullptr) {
        LOG_ERR("[DFX - ProcessDetect] - sendtoHost is null!");
    }
    m_sendtoHost = sendtoHost;
}

void ProcessDetect::Deinit()
{
    if (m_sendtoHost != nullptr) {
        m_sendtoHost.reset();
    }
}

void ProcessDetect::Run()
{
    LOG_INFO("[DFX - ProcessDetect] - Run");
    GetMonitorProcessInfo();
}

void ProcessDetect::Stop()
{
    LOG_INFO("[DFX - ProcessDetect] - Stop");
    std::lock_guard<std::mutex> lock(objectlock);
    vectorProcessLifeRecord.clear();
}

void ProcessDetect::OnRecvProcessMsg(DWORD processId, ProcessOperation type)
{
    std::string processName = "";
    if (type == PROCESS_CREATE) {
        std::wstring fullPath;
        bool ret = PERF_UTILS.QueryProcessNameByPid(processId, fullPath);
        if (!ret) {
            return;
        }
        processName = StringProc::WStringToStringByUTF8(fullPath);
        {
            std::lock_guard<std::mutex> lk(procNameInfoMutex);
            procNameInfo[processId] = processName;
        }
    } else if (type == PROCESS_CLOSE) {
        std::lock_guard<std::mutex> lk(procNameInfoMutex);
        if (procNameInfo.find(processId) != procNameInfo.end()) {
            processName = procNameInfo[processId];
            procNameInfo.erase(processId);
        }
    }
    SaveProcessInfo(processId, processName, type);
    ClearExpireData();
    SyncWinUpdateProcStatus(processName, type);
}

std::string ProcessDetect::GetProcessNameFromFullPath(const std::string& path)
{
    if (path.empty()) {
        return path;
    }
    std::string processName = path;
    std::string::size_type idx = path.rfind("\\");
    if (idx != path.npos) {
        processName = processName.substr(idx + 1);
    }
    return processName;
}

void ProcessDetect::SaveProcessInfo(DWORD processId, std::string processName, ProcessOperation operationType)
{
    if (operationType != PROCESS_CREATE && operationType != PROCESS_CLOSE) {
        return;
    }
    std::string exeFileName = GetProcessNameFromFullPath(processName);
    if (exeFileName == "") {
        return;
    }
    std::wstring monProcessName = StringProc::StringToWString(exeFileName);
    // 进程关闭， 不要再去查签名了
    if (operationType == PROCESS_CLOSE) {
        SetProcessCloseTime(processId, monProcessName);
        return;
    }
    // 签名先忽略，只匹配exe名称
    std::lock_guard<std::mutex> lock(monitorProcessMtx);
    if (monitorProcess.find(exeFileName) != monitorProcess.end()) {
        ProcessLifeRecord processLifeRecordItem;
        processLifeRecordItem.processName = monProcessName;
        processLifeRecordItem.processId = processId;
        processLifeRecordItem.processCreateTime = time(nullptr);
        SetToVector(processLifeRecordItem);
    }
}

void ProcessDetect::SetToVector(const ProcessLifeRecord& objProcessLifeRecord)
{
    std::lock_guard<std::mutex> lock(objectlock);
    bool isFind = false;
    for (const auto& procLifeRecord : vectorProcessLifeRecord) {
        if (procLifeRecord.processId == objProcessLifeRecord.processId &&
            procLifeRecord.processName == objProcessLifeRecord.processName) {
            isFind = true;
            break;
        }
    }
    if (!isFind) {
        vectorProcessLifeRecord.push_back(objProcessLifeRecord);
    }
}

void ProcessDetect::SetProcessCloseTime(DWORD pid, const std::wstring& processName)
{
    std::lock_guard<std::mutex> lock(objectlock);
    for (auto& procLifeRecord : vectorProcessLifeRecord) {
        if (procLifeRecord.processId == pid && procLifeRecord.processName == processName) {
            procLifeRecord.processCloseTime = time(NULL);
        }
    }
}

void ProcessDetect::ClearExpireData()
{
    std::lock_guard<std::mutex> lock(objectlock);
    auto itr = vectorProcessLifeRecord.begin();
    for (; itr != vectorProcessLifeRecord.end();) {
        if (((itr->processCloseTime > 0) && ((time(nullptr) - itr->processCloseTime) >= HOLD_PROCESS_DATA_TIME)) ||
            ((itr->processCloseTime == 0) && !PERF_UTILS.IsProcessExists(itr->processId))) {
            itr = vectorProcessLifeRecord.erase(itr);
            continue;
        }
        itr++;
    }
}

std::vector<ProcessLifeRecord> ProcessDetect::GetProcessLifeInfo()
{
    std::lock_guard<std::mutex> lock(objectlock);
    return vectorProcessLifeRecord;
}

void ProcessDetect::GetMonitorProcessInfo()
{
    // 读取配置文件中进程白名单信息
    std::lock_guard<std::mutex> lock(monitorProcessMtx);
    monitorProcess.clear();
    cJSON *root = cJSON_Parse(ReadAppUsingMonitorList().c_str());
    if (!root) {
        LOG_ERR("[DFX - ProcessDetect] - parse config file failed");
        monitorProcess.clear();
        return;
    }
    cJSON* config = cJSON_GetObjectItem(root, "config");
    if (!config) {
        LOG_ERR("[DFX - ProcessDetect] - config file invalid");
        cJSON_Delete(root);
        monitorProcess.clear();
        return;
    }
    cJSON* softwareList = cJSON_GetObjectItem(config, "SoftWareList");
    if (!softwareList) {
        LOG_ERR("[DFX - ProcessDetect] - config file invalid");
        cJSON_Delete(root);
        monitorProcess.clear();
        return;
    }
    cJSON* software = cJSON_GetObjectItem(softwareList, "Software");
    if (!software) {
        LOG_ERR("[DFX - ProcessDetect] - config file invalid");
        cJSON_Delete(root);
        monitorProcess.clear();
        return;
    }
    int listSize = cJSON_GetArraySize(software);
    for (int i = 0; i < listSize; i++) {
        cJSON* softwareItem = cJSON_GetArrayItem(software, i);
        if (!softwareItem) {
            continue;
        }
        cJSON* process = cJSON_GetObjectItem(softwareItem, "Process");
        if (!process) {
            continue;
        }
        int processSize = cJSON_GetArraySize(process);
        for (int j = 0; j < processSize; j++) {
            cJSON* processItem = cJSON_GetArrayItem(process, j);
            if (!processItem) {
                continue;
            }
            cJSON* processInfo = cJSON_GetObjectItem(processItem, "PName");
            if (processInfo != nullptr && cJSON_IsString(processInfo) && processInfo->valuestring != nullptr) {
                monitorProcess.insert(processInfo->valuestring);
            }
        }
    }
    cJSON_Delete(root);
}

std::string ProcessDetect::ReadAppUsingMonitorList()
{
    LOG_INFO("[DFX - ProcessDetect] - begin.");
    HANDLE hConfigFile = CreateFile(PERF_SOFTWARE_WHITE_LIST_PATH.c_str(), GENERIC_READ, 0, nullptr, OPEN_EXISTING,
                                    FILE_ATTRIBUTE_NORMAL, nullptr);
    if (hConfigFile == INVALID_HANDLE_VALUE) {
        LOG_ERR("[DFX - ProcessDetect] - error occurred, file handle is nullptr, error code: {}",
                GetLastError());
        return "{}";
    }
    DWORD size = GetFileSize(hConfigFile, nullptr);
    DWORD sizeReaded = 0;
    char *data = new (std::nothrow) char[size + 1];
    if (data == nullptr) {
        LOG_ERR("[DFX - ProcessDetect] - error occurred, allocate memory failed!");
        if (hConfigFile != INVALID_HANDLE_VALUE) {
            CloseHandle(hConfigFile);
            hConfigFile = INVALID_HANDLE_VALUE;
        }
        return "{}";
    }
    SecureZeroMemory(data, size + 1);
    if (!ReadFile(hConfigFile, data, size, &sizeReaded, nullptr) || sizeReaded != size) {
        LOG_ERR("[DFX - ProcessDetect] - error occurred, reading file failed, error code: {}", GetLastError());
        if (hConfigFile != INVALID_HANDLE_VALUE) {
            CloseHandle(hConfigFile);
            hConfigFile = INVALID_HANDLE_VALUE;
        }
        delete[] data;
        return "{}";
    }
    if (hConfigFile != INVALID_HANDLE_VALUE) {
            CloseHandle(hConfigFile);
            hConfigFile = INVALID_HANDLE_VALUE;
        }
    std::string config = data;
    delete[] data;
    return config;
}

void ProcessDetect::SyncWinUpdateProcStatus(std::string processName, ProcessOperation type)
{
    if (processName.find(WIN_UPDATE_CORE_PROCESS) == std::string::npos) {
        return;
    }
    if (type == PROCESS_CREATE) {
        LOG_INFO("[DFX - ProcessDetect] - wuaucltcore start: {}", processName.c_str());
        m_sendtoHost->SyncUpdateProcStatus(true);
    } else if (type == PROCESS_CLOSE) {
        LOG_INFO("[DFX - ProcessDetect] - wuaucltcore stop: {}", processName.c_str());
        m_sendtoHost->SyncUpdateProcStatus(false);
    } else {
        LOG_INFO("[DFX - ProcessDetect] - wuaucltcore does other operation");
    }
}
}